namespace yeushu {
    // * 在函数内部使用泛型变量的时候，由于事先不知道它是哪种类型，所以不能随意的操作它的属性或方法：

    type HasZks = {
        zks: (a: string, b: string) => void
    }

    function getZks<T extends HasZks>(ZKS: T) {
        return ZKS.zks("1", "2")
    }

    let UseZks = {
        zks: function (a: string, b: string) {
            console.log(a + b)
        }
    }


    class SSS {
        name: string = "s"

        zks(a: string, b: string) {
            console.log(a + b)
        }
    }

    getZks(UseZks)
    getZks(new SSS())


    // 约束一个泛型必须具备split方法
    interface HasSplit {
        split(separator: string): string[];
    }

    // interface hasLength {
    //     // length: number;
    //     // 约束传递为一个有toupperlowcase()的方法
    //     toUpperCase(): string
    // }
    type hasLength = {
        length: number;
        toUpperCase(): string
    }

    function getlegnth<T extends hasLength>(x: T): string {
        return x.toUpperCase();
    }

    let szs = getlegnth("456");

    // console.log(getlegnth(255))

    function minimumLength<Type extends hasLength>(
        obj: Type,
        minimum: number
    ): number | Type {
        if (obj.length >= minimum) {
            return obj;
        } else {
            return minimum;
        }
    }

    minimumLength([1, 2, 3], 6);

    // 多个类型参数之间也可以互相约束：
    // 下例中，我们使用了两个类型参数，其中要求 T 继承 U，这样可以保证 U 取值必须在T中含有。
    // 泛型通配符可以利用类的继承来理解 T extend U U是父类 那么 T继承自U 必须有U的属性且可以扩展
    function copyFields<T extends U, U>(target: T, source: U): T {
        for (let id in source) {
            target[id] = (source as T)[id];
        }
        return target;
    }

    function copyFields2<T, U extends T>(target: T, source: U): boolean {
        for (const key in target) {
            if (target[key] === source[key]) {
                return true
            }
        }
        return false
    }

    let x = {a: 1, b: 2, c: 3, d: 4, e: 60};

    copyFields(x, {c: 10})

    copyFields2({c: 10}, x)

    // console.log(copyFields(x, {a: 10}));

    function changeID<T, U extends keyof T>(target: T, source: U): T[U] {
        return target[source];
    }

    const obj = {a: 1, b: 2, c: 3, d: 4, e: 60};

    console.log(changeID(obj, "b"));


    const arr = [1, 2, 3];

    type ElementType<T> = T extends (infer U)[] ? U : never;
    type a = ElementType<typeof arr>;
    type sdasd = typeof arr

// 测试
    const value: a = 2; // 这将被正确推断为 number 类型

}
